#include "stdafx.h"
#include <stdlib.h>
#include <vcclr.h>
#include "problem_solver_core.h"
#include "problem.h"
#include "solver.h"
#include "Problem Form.h"
#include "tag_info.h"

namespace nnse
{
	io_module io_m("startup.txt", "", "", "", "", "", "cerr.txt");

	// Constructor and Destructor

	template <class T, class U> problem<T, U>::problem()
	{
		io_m.startup_stream << "Constructing problem" << std::endl;
		file_drive="";
		file_dir="";
		file_name="";
		file_ext="";
		problem_solver=nullptr;
		problem_form=gcnew SimpleGUI::Problem<T, U>(this);
	}

	template <class T, class U> problem<T, U>::~problem()
	{
		io_m.startup_stream << "Destructing problem" << std::endl;
	}

	// Accessor Functions

	template <class T, class U> inline System::String^ problem<T, U>::get_file()
	{
		return gcnew System::String(file_drive + file_dir + file_name + file_ext);
	}

	template <class T, class U> inline System::String^ problem<T, U>::get_file_name()
	{
		return gcnew System::String(file_name);
	}

	template <class T, class U> inline System::String^ problem<T, U>::get_file_ext()
	{
		return gcnew System::String(file_ext);
	}

	template <class T, class U> inline void problem<T, U>::put_file(System::String^ f_s)
	{
		pin_ptr<const wchar_t> file_spec=PtrToStringChars(f_s);
		wchar_t file_drive[500];
		wchar_t file_dir[500];
		wchar_t file_name[500];
		wchar_t file_ext[500];
		_wsplitpath_s(file_spec, file_drive, 500, file_dir, 500, file_name, 500, file_ext, 500);
		this->file_drive=gcnew System::String(file_drive);
		this->file_dir=gcnew System::String(file_dir);
		this->file_name=gcnew System::String(file_name);
		this->file_ext=gcnew System::String(file_ext);
	}

	template <class T, class U> inline solver<T, U>% problem<T, U>::get_solver()
	{
		if (problem_solver)
		{
			return *problem_solver;
		}
		else
		{
			throw std::out_of_range("\nError in problem<T, U>::get_solver");
		}
	}

	template <class T, class U> inline void problem<T, U>::put_solver()
	{
		try
		{
			problem_solver=gcnew solver<T, U>(this, "Character Recognition");
		}
		catch(const std::exception& error)
		{
			io_m.cerr_stream << error.what() << std::endl;
		}
	}

	// Worker Functions

	template <class T, class U> void problem<T, U>::load(System::String^ f_s)
	{
		put_file(f_s);
		pin_ptr<const wchar_t> file_spec=PtrToStringChars(f_s);
		std::ifstream file_stream;
		if (typeid(T)==typeid(bool))
		{
			file_stream.open(file_spec);
		}
		if (typeid(T)==typeid(unsigned char))
		{
			file_stream.open(file_spec, std::ios::binary);
		}
		read_header(file_stream);
		read_data(file_stream);
		file_stream.close();
	}

	template <class T, class U> void problem<T, U>::save(System::String^ f_s)
	{
		put_file(f_s);
		pin_ptr<const wchar_t> file_spec=PtrToStringChars(f_s);
		std::ofstream file_stream;
		file_stream.open(file_spec);
		write_header(file_stream);
		write_data(file_stream);
		file_stream.close();
	}

	template <class T, class U> void problem<T, U>::create()
	{
		put_file("");
		put_current_training_example(0);
		put_current_testing_example(0);
		allocate_all_storage();
	}

	template <> void problem<bool, double>::read_header(std::ifstream& file_stream)
	{
		char text[500];
		file_stream.getline(text, 500);
		unsigned int i;
		file_stream >> i;
		put_number_of_training_examples(i);
		file_stream >> i;
		put_number_of_testing_examples(i);
		file_stream >> i;
		put_current_training_example(i);
		file_stream >> i;
		put_current_testing_example(i);
		file_stream >> i;
		put_number_of_inputs(i);
		file_stream >> i;
		put_number_of_targets(i);
		file_stream >> i;
		put_wrap_input(i);
		file_stream >> i;
		put_wrap_target(i);
		allocate_all_storage();
		file_stream.getline(text, 500);
		for (unsigned int t=0; t<get_number_of_targets(); t++)
		{
			file_stream.getline(text, 500);
			target_comments[t]=gcnew System::String(text);
		}
	}

	template <> void problem<unsigned char, double>::read_header(std::ifstream& file_stream)
	{
		four_byte_int number;
		file_stream.read(number.c, 4);
		byte_swap(number);
		unsigned int magic_number=number.i;
		file_stream.read(number.c, 4);
		byte_swap(number);
		unsigned int number_images=100; // number.i;
		if (get_file_name()=="train-images-idx3-ubyte" || get_file_name()=="train-labels-idx1-ubyte")
		{
			put_training_flag();
			put_number_of_training_examples(number_images);
			put_current_training_example(0);
		}
		if (get_file_name()=="t10k-images-idx3-ubyte" || get_file_name()=="t10k-labels-idx1-ubyte")
		{
			put_testing_flag();
			put_number_of_testing_examples(number_images);
			put_current_testing_example(0);
		}
		if (get_file_ext()==".idx3")
		{
			put_input_flag();
			file_stream.read(number.c, 4);
			byte_swap(number);
			unsigned int number_rows=number.i;
			file_stream.read(number.c, 4);
			byte_swap(number);
			unsigned int number_cols=number.i;
			put_number_of_inputs(number_rows*number_cols);
			put_wrap_input(number_cols);
		}
		if (get_file_ext()==".idx1")
		{
			put_target_flag();
			put_number_of_targets(10);
			put_wrap_target(10);
		}
		allocate_storage();
		for (unsigned int t=0; t<get_number_of_targets(); t++)
		{
			char c='0'+t;
			target_comments[t]=gcnew System::String(&c);
		}
	}
	
	template <> void problem<bool, double>::write_header(std::ofstream& file_stream)
	{
		file_stream << "Header" << std::endl;
		file_stream << get_number_of_training_examples() << " ";
		file_stream << get_number_of_testing_examples() << " ";
		file_stream << get_current_training_example() << " ";
		file_stream << get_current_testing_example() << " ";
		file_stream << get_number_of_inputs() << " ";
		file_stream << get_number_of_targets() << " ";
		file_stream << get_wrap_input() << " ";
		file_stream << get_wrap_target() << std::endl;
		for (unsigned int t=0; t<get_number_of_targets(); t++)
		{
			pin_ptr<const wchar_t> comment=PtrToStringChars(target_comments[t]);
			wchar_t new_comment[500];
			wcscpy_s(new_comment, 500, comment);
			size_t i;
			char new_new_comment[500];
			wcstombs_s(&i, new_new_comment, 500, new_comment, _TRUNCATE);
			file_stream << new_new_comment << std::endl;
		}
	}

	template <> void problem<unsigned char, double>::write_header(std::ofstream& file_stream)
	{
	}

	template <> void problem<bool, double>::read_data(std::ifstream& file_stream)
	{
		char text[500];
		file_stream.getline(text, 500);
		put_training_flag();
		put_input_flag();
		read_part_data(file_stream);
		put_training_flag();
		put_target_flag();
		read_part_data(file_stream);
		put_testing_flag();
		put_input_flag();
		read_part_data(file_stream);
		put_testing_flag();
		put_target_flag();
		read_part_data(file_stream);
	}

	template <> void problem<unsigned char, double>::read_data(std::ifstream& file_stream)
	{
		read_part_data(file_stream);
		normalise_vector();
	}

	template <> void problem<bool, double>::write_data(std::ofstream& file_stream)
	{
		file_stream << "Data" << std::endl;
		put_training_flag();
		put_input_flag();
		write_part_data(file_stream, "Training input vectors");
		put_training_flag();
		put_target_flag();
		write_part_data(file_stream, "Training target vectors");
		put_testing_flag();
		put_input_flag();
		write_part_data(file_stream, "Testing input vectors");
		put_testing_flag();
		put_target_flag();
		write_part_data(file_stream, "Testing target vectors");
	}

	template <> void problem<unsigned char, double>::write_data(std::ofstream& file_stream)
	{
	}

	template <> void problem<bool, double>::read_part_data(std::ifstream& file_stream)
	{
		char text[500];
		file_stream.getline(text, 500);
		for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
		{
			put_current_example(ex);
			file_stream.getline(text, 500);
			put_comment(gcnew System::String(text));
			for (unsigned int el=0; el<get_number_of_elements(); el++)
			{
				bool b;
				file_stream >> b;
				put_value(el, b);
				unsigned int next_el=el+1;
				SimpleGUI::tag_info next_tag(next_el, get_wrap_element());
				if (next_tag.get_column_index()==0 || next_el==get_number_of_elements())
				{
					file_stream.getline(text, 500);
				}
			}
			problem_form->update_form("Loading...", 0, ex, get_number_of_examples());
		}
	}
	
	template <> void problem<unsigned char, double>::read_part_data(std::ifstream& file_stream)
	{
		for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
		{
			put_current_example(ex);
			if (get_file_ext()==".idx3")
			{
				for (unsigned int el=0; el<get_number_of_inputs(); el++)
				{
					unsigned char c[1];
					file_stream.read(reinterpret_cast<char*>(c), 1);
					put_value(el, *c);
				}
			}
			if (get_file_ext()==".idx1")
			{
				unsigned char c[1];
				file_stream.read(reinterpret_cast<char*>(c), 1);
				for (unsigned int el=0; el<get_number_of_targets(); el++)
				{
					if (static_cast<unsigned char>(el)==*c)
					{
						put_value(el, 1);
						put_comment(target_comments[el]);
					}
					else
					{
						put_value(el, 0);
					}
				}
			}
			if (ex%10==0)
			{
				problem_form->update_form("Loading...", 0, ex, get_number_of_examples());
			}
		}
	}

	template <> void problem<bool, double>::write_part_data(std::ofstream& file_stream, const char* description)
	{
		file_stream << description << std::endl;
		for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
		{
			put_current_example(ex);
			pin_ptr<const wchar_t> comment=PtrToStringChars(get_comment());
			wchar_t new_comment[500];
			wcscpy_s(new_comment, 500, comment);
			size_t i;
			char new_new_comment[500];
			wcstombs_s(&i, new_new_comment, 500, new_comment, _TRUNCATE);
			file_stream << new_new_comment << std::endl;
			for (unsigned int el=0; el<get_number_of_elements(); el++)
			{
				bool b=static_cast<bool>(get_value(el));
				file_stream << b;
				unsigned int next_el=el+1;
				SimpleGUI::tag_info next_tag(next_el, get_wrap_element());
				if (next_tag.get_column_index()==0 || next_el==get_number_of_elements())
				{
					file_stream << std::endl;
				}
				else
				{
					file_stream << " ";
				}
			}
		}
	}

	template <> void problem<unsigned char, double>::write_part_data(std::ofstream& file_stream, const char* description)
	{
	}

	// Explicit Instantiation

	template ref class problem<bool, double>;
	template ref class problem<unsigned char, double>;
}
